package cn.blnp.net.flowable.event.bound.error;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * <h3>边界事件-错误边界事件案例</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/8 17:49
 */
@Slf4j
@SpringBootTest
public class BoundErrorEvent {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;

    /**
     *  <p>
     *      错误边界事件依附在某个流程活动中，用于捕获定义于该活动作用域内的错误。错误边界事件通常用于嵌入子流程或者调用活动，也可以用于其他节点。当错
     *  误边界事件依附的流程活动抛出BpmnError错误后，错误事件被捕获，错误边界事件被触发，所依附的流程活动被终止，流程继续沿着错误边界事件的外出顺序流流转。
     *  具体事件定义格式为：
     *  <pre>
     *      {@code
     *          <!-- 定义BPMN错误 -->
     *          <error id="theError" errorCode="410" />
     *          <process id="errorBoundaryEventProcess">
     *              <!-- 定义用户任务节点 -->
     *              <userTask id="theUserTask" name="审批"/>
     *              <!-- 定义边界事件 -->
     *              <boundaryEvent id="errorBoundaryEvent" name="错误边界事件" attachedToRef="theUserTask">
     *                  <!-- 包含errorEventDefinition子元素，代表错误边界事件 -->
     *                  <errorEventDefinition errorRef="theError"/>
     *              </boundaryEvent>
     *          </process>
     *      }
     *  </pre>
     *  </p>
     *  <br/>
     *  <h2>演示案例</h2>
     *  <hr/>
     *  <p>
     *      <b>流程定义文件：</b>{@code flow/boundErrorEvent.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://free4.yunpng.top/2025/05/08/681c812e0b372.png" />
     *  </p><br/>
     *  <h2 style="color:red;">特别注意：</h2>
     *  <p>
     *      当流程中要对不同的BPMN错误使用不同的错误处理逻辑时，可在一个流程活动上定义多个错误边界事件。对于每个错误边界事件，需要使用不同的错误代码来区分错误类型。
     *   错误边界事件通过设置其errorRef属性引用BPMN错误，BPMN错误的errorCode属性用于查询符合查询条件的错误捕获边界事件。具体规则如下：
     *  </p>
     *  <ol>
     *      <li>如果错误边界事件的错误事件定义设置了errorRef属性，并引用了一个已定义的BPMN错误，则仅捕获errorCode与之相同的错误；</li>
     *      <li>如果错误边界事件的错误事件定义设置了errorRef属性，但不匹配任何已定义的错误，则errorRef会作为errorCode使用；</li>
     *      <li>如果错误边界事件的错误事件定义没有设置errorRef属性，错误边界事件将会捕获任何BPMN错误，无论错误的errorCode是什么。</li>
     *  </ol>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("错误边界事件案例")
                .addClasspathResource("flow/boundErrorEvent.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());
        //查询并完成第一个任务
        Task firstTask = taskQuery.singleResult();
        log.info("第一个用户任务为：{}", firstTask.getName());
        Map<String, Object> varMap = MapUtil.builder(new HashMap<String, Object>())
                        .put("healthCodeStatus", "red")
                                .build();
        taskService.complete(firstTask.getId(), varMap);
        //查询第二个任务
        Task secondTask = taskQuery.singleResult();
        log.info("第二个用户任务为: {}", secondTask.getName());
        //完成第二个任务
        taskService.complete(secondTask.getId());

        /**
         *  演示结果：
         *  2025-05-08 18:07:23.152  INFO 80788 --- [           main] c.b.n.f.e.bound.error.BoundErrorEvent    : 部署流程定义：3bfebc9b-2bf4-11f0-9d92-50ebf6ec8f6d
         * 2025-05-08 18:07:23.160  INFO 80788 --- [           main] c.b.n.f.e.bound.error.BoundErrorEvent    : 流程定义编号：boundErrorEvent:1:3c3fbd3e-2bf4-11f0-9d92-50ebf6ec8f6d
         * 2025-05-08 18:07:23.214  INFO 80788 --- [           main] c.b.n.f.e.bound.error.BoundErrorEvent    : 第一个用户任务为：提交材料
         * 2025-05-08 18:07:23.238 ERROR 80788 --- [           main] c.b.n.f.e.b.e.AutomaticReviewService     : 健康码异常，抛出BPMN错误，errorCode为：healthCodeNotGreen
         * 2025-05-08 18:07:23.281  INFO 80788 --- [           main] c.b.n.f.e.bound.error.BoundErrorEvent    : 第二个用户任务为: 人工复审
         **/
    }
}
